package parse

import (
	"fmt"
	"showen7/ast"
	"showen7/tok"
)

type Parser struct {
	tokens []tok.Token
	//current      int
	peek         int
	currentToken tok.Token
	peekToken    tok.Token
}

func NewParser(tokens []tok.Token) *Parser {
	if len(tokens) == 0 {
		tokens = []tok.Token{{Type: tok.EOF}, {Type: tok.EOF}}
	} else if len(tokens) == 1 {
		tokens = append(tokens, tok.Token{Type: tok.EOF})
	}
	p := &Parser{
		tokens:       tokens,
		currentToken: tokens[0],
		peekToken:    tokens[1],
		peek:         1,
	}
	return p
}

func (p *Parser) nextToken() {
	p.currentToken = p.peekToken
	if p.peek >= len(p.tokens)-1 {
		return
	}
	p.peek++
	p.peekToken = p.tokens[p.peek]
}

func (p *Parser) ParseProgram() ([]ast.Exp, error) {
	exprs := []ast.Exp{}
	for p.currentToken.Type != tok.EOF {
		expr, err := p.ParseExpression()
		if expr == nil && err == nil {
			break
		}
		if err != nil {
			return nil, err
		}
		exprs = append(exprs, expr)
	}
	return exprs, nil
}

func (p *Parser) ParseExpression() (ast.Exp, error) {
	var exp ast.Exp
	var err error

	for {
		switch p.currentToken.Type {
		case tok.EOF:
			return nil, nil
		case tok.ILLEGAL:
			return nil, fmt.Errorf("line %v col %v: illegal token %s", p.currentToken.Line, p.currentToken.Column, p.currentToken.Lexeme)
		case tok.LPAREN:
			exp, err = p.parseList()
		case tok.VECTOR:
			exp, err = p.parseVector()
			p.nextToken()
		case tok.QUOTE:
			exp, err = p.parseQuote()
			// Don't call p.nextToken() here, parseQuote already advances past the quoted expression
		case tok.QUASIQUOTE:
			exp, err = p.parseQuasiquote()
			// Don't call p.nextToken() here, parseQuasiquote already advances past the quasiquoted expression
		case tok.UNQUOTE:
			exp, err = p.parseUnquote()
			// Don't call p.nextToken() here, parseUnquote already advances past the unquoted expression
		case tok.UNQUOTE_SPLICING:
			exp, err = p.parseUnquoteSplicing()
			// Don't call p.nextToken() here, parseUnquoteSplicing already advances past the unquote-spliced expression
		case tok.RPAREN:
			return nil, fmt.Errorf("line %v col %v: unexpected token %s", p.currentToken.Line, p.currentToken.Column, p.currentToken.Lexeme)
		case tok.IDENTIFIER:
			exp = &ast.IdentifierExp{Token: p.currentToken}
			p.nextToken()
		case tok.INTEGER, tok.REAL, tok.RATIONAL, tok.COMPLEX:
			exp = &ast.NumberExp{Token: p.currentToken}
			p.nextToken()
		case tok.STRING:
			exp = &ast.StringExp{Token: p.currentToken}
			p.nextToken()
		case tok.CHARACTER:
			exp = &ast.CharacterExp{Token: p.currentToken}
			p.nextToken()
		case tok.BOOLEAN:
			exp = &ast.BooleanExp{Token: p.currentToken}
			p.nextToken()
		case tok.COMMENT:
			p.nextToken()
			continue // Skip comments and try the next token
		default:
			return nil, fmt.Errorf("line %v col %v: unexpected token %s", p.currentToken.Line, p.currentToken.Column, p.currentToken.Lexeme)
		}

		if err != nil {
			return nil, err
		}

		return exp, nil
	}
}

func (p *Parser) parseQuote() (ast.Exp, error) {
	token := p.currentToken
	p.nextToken() // advance past the quote

	// Parse the quoted expression
	expr, err := p.ParseExpression()
	if err != nil {
		return nil, err
	}
	if expr == nil {
		return nil, fmt.Errorf("expected expression after quote")
	}

	quoteExp := &ast.QuoteExp{
		Token: token,
		Value: expr,
	}

	return quoteExp, nil
}

func (p *Parser) parseQuasiquote() (ast.Exp, error) {
	token := p.currentToken
	p.nextToken() // advance past the quasiquote

	// Parse the quasiquoted expression
	expr, err := p.ParseExpression()
	if err != nil {
		return nil, err
	}
	if expr == nil {
		return nil, fmt.Errorf("expected expression after quasiquote")
	}

	quasiquoteExp := &ast.QuasiquoteExp{
		Token: token,
		Value: expr,
	}

	return quasiquoteExp, nil
}

func (p *Parser) parseUnquote() (ast.Exp, error) {
	token := p.currentToken
	p.nextToken() // advance past the unquote

	// Parse the unquoted expression
	expr, err := p.ParseExpression()
	if err != nil {
		return nil, err
	}
	if expr == nil {
		return nil, fmt.Errorf("expected expression after unquote")
	}

	unquoteExp := &ast.UnquoteExp{
		Token: token,
		Value: expr,
	}

	return unquoteExp, nil
}

func (p *Parser) parseUnquoteSplicing() (ast.Exp, error) {
	token := p.currentToken
	p.nextToken() // advance past the unquote-splicing

	// Parse the unquote-spliced expression
	expr, err := p.ParseExpression()
	if err != nil {
		return nil, err
	}
	if expr == nil {
		return nil, fmt.Errorf("expected expression after unquote-splicing")
	}

	unquoteSplicingExp := &ast.UnquoteSplicingExp{
		Token: token,
		Value: expr,
	}

	return unquoteSplicingExp, nil
}

func (p *Parser) parseList() (ast.Exp, error) {
	// Advance past the opening parenthesis
	p.nextToken()

	// Check for special forms
	if p.currentToken.Type == tok.IDENTIFIER {
		switch p.currentToken.Lexeme {
		case "quote":
			exp, err := p.parseQuote()
			// Advance past the closing parenthesis
			p.nextToken()
			return exp, err
		case "quasiquote":
			exp, err := p.parseQuasiquote()
			// Advance past the closing parenthesis
			p.nextToken()
			return exp, err
		}
	}

	// Create a list to hold the elements
	list := &ast.ListExp{Exps: []ast.Exp{}}

	// Parse elements until we reach the closing parenthesis
	for p.currentToken.Type != tok.RPAREN {
		if p.currentToken.Type == tok.EOF {
			return nil, fmt.Errorf("unexpected end of line %v, expected ')'", p.currentToken.Line)
		}

		// Parse the current expression
		expr, err := p.ParseExpression()
		if err != nil {
			return nil, err
		}

		// Add the expression to our list
		list.Exps = append(list.Exps, expr)

		// The ParseExpression function already advances the token, so we don't need to do it here
		// p.nextToken()
	}

	// Advance past the closing parenthesis
	p.nextToken()

	return list, nil
}

func (p *Parser) parseVector() (ast.Exp, error) {
	// Advance past the opening "#("
	p.nextToken()

	// Create a vector to hold the elements
	vector := &ast.VectorExp{Exps: []ast.Exp{}}

	// Parse elements until we reach the closing parenthesis
	for p.currentToken.Type != tok.RPAREN {
		if p.currentToken.Type == tok.EOF {
			return nil, fmt.Errorf("unexpected end of line %v, expected ')'", p.currentToken.Line)
		}

		// Parse the current expression
		expr, err := p.ParseExpression()
		if err != nil {
			return nil, err
		}

		// Add the expression to our vector
		vector.Exps = append(vector.Exps, expr)

		// The ParseExpression function already advances the token, so we don't need to do it here
		// p.nextToken()
	}

	return vector, nil
}
